from flup.server.fcgi import WSGIServer
from cgi import parse_qs, escape
from webPresentation import WebPresentation
import dbase
from os.path import expanduser, getsize, abspath, exists, dirname, isdir
import string
from json import loads, dumps
import logging
from time import time
from wsgiref.handlers import format_date_time
import utils
import pdriveStrings
from totp import TOTP

def runServer(lighttpdSocket, sqliteDbaseFile, secureDLKey):
	try:
		logging.info("Spinning up fcgi server")
		httpd(lighttpdSocket, sqliteDbaseFile, secureDLKey)
	except:
		raise

class httpd:

	class ExceptionCriticalError:
		def __init__(self, status, errorMessage, date):
			self.status = status
			self.errorMessage = errorMessage
			self.date = date
		def __str__(self):
			return errorMessage
	_socket = None
	_sqliteDbaseFile = None
	_pdriveFiles = None
	_pdriveRoot = None
	_secureDownloadKey = None

	def _checkSignature(self, xSignature, command, psk, xDate, username):
		if username is None:
			return False
		
		logging.debug("xSignature ({}) command ({}) psk ({}) xDate({}) username ({})".format(xSignature, command, psk, xDate, username))
	
		if xSignature != utils.hmacSHA256(utils.hmacSHA256(command, psk), utils.hmacSHA256(xDate, username)):
			return False
		return True

	def _getBadSigResponse(self, command):
		return dumps({
			"status": "ERR",
			"command": command,
			"code": 401,
			"message": "Bad signature"
			})

	def __init__(self, socket, sqliteDbaseFile, secureDLKey):
		self._socket = socket
		self._sqliteDbaseFile = sqliteDbaseFile
		self._secureDownloadKey = secureDLKey


		WSGIServer(self._entry, bindAddress = self._socket).run()
	
        def _entry(self, environ, start_response):
		xDate = environ.get('HTTP_X_DATE', None)
		xSignature = environ.get('HTTP_X_SIGNATURE', None)
		#logging.debug(environ)
		logging.debug("New httpd entry thread started")
		logging.debug("Refererd From ({}) Method ({}) Query String ({}) xDate ({}) xSig ({})".format(
			environ.get('HTTP_REFERER', ''), 
			environ.get('REQUEST_METHOD', ''),
			environ.get('QUERY_STRING', ''),
			xDate,
			xSignature))

		## Do the pre-reqs

		## Default to json content
		contentType = "application/json"

		## Connect to dbase
		if self._sqliteDbaseFile.startswith('~'):
			self._sqliteDbaseFile = expanduser(sqliteDbaseFile)
		try:
			db = dbase.DBase(self._sqliteDbaseFile)
		except dbase.CantConnect as e:
			logging.debug("Cannot connect to sqlite database file ({}): {}".format(self._sqliteDbaseFile, e))
			raise SystemExit
		
		## Load up basic config keys
		self._pdriveFiles = db.fetchKeyValue("pdrive_files")
		self._pdriveRoot = db.fetchKeyValue("pdrive_root")

		## Get a web presentation instance
		wp = WebPresentation()

		## Determine the type of request and process	
		response = ""
                status = "200 OK"
		username = None
		try:
			cmd = string.lstrip(environ["PATH_INFO"], "/")
			if environ.get('REQUEST_METHOD', '') == "POST":

				try:
					requestBodySize = int(environ.get('CONTENT_LENGTH', 0))
				except (ValueError):
					requestBodySize = 0
				logging.debug("Body size ({})".format(requestBodySize))
				if requestBodySize > 0:
					requestBody = parse_qs(environ['wsgi.input'].read(requestBodySize))
					logging.debug("Request Body type ({}) ({})".format(type(requestBody), requestBody))
					username = requestBody.get("username", [None])[0]
					logging.debug("Got username from post ({})".format(username))
				else:
					requestBody = None

				logging.debug("Command ({})".format(cmd))
				try:
					username = requestBody.get("username", [None])[0]
					psk = db.fetchPSK(username)["secret"]
				except:
					username = None
					psk = None
				##
				# getMediaList
				##
				if cmd == "getMediaList":
					logging.debug("Got POST command '{}'".format(cmd))
					if self._checkSignature(xSignature, cmd, psk, xDate, username) is True:
						mediaTypeLimit = requestBody.get("mediaTypeLimit", [None])[0]
						fileTypeLimit = requestBody.get("fileTypeLimit", [None])[0]
						textLimit = requestBody.get("textLimit", [None])[0]
						tagList = requestBody.get("tagList", [None])[0]
						sha256Hash = requestBody.get("sha256Hash", [None])[0]
						logging.debug("mediaTypeLimit ({}) fileTypeLimit ({}) textLimit ({}) tagList ({}) sha256Hash ({})".format(
							mediaTypeLimit, fileTypeLimit, textLimit, tagList, sha256Hash))
						mediaList = wp.getMediaList(
							db, 
							sha256Hash = sha256Hash, 
							mediaTypeLimit = mediaTypeLimit, 
							fileTypeLimit = fileTypeLimit, 
							search = textLimit, 
							tagList = tagList,
							secureDownloadKey = self._secureDownloadKey
						)
						response = dumps({
							"status": "OK",
							"command": cmd,
							"mediaList": mediaList, 
							"mediaListCount": len(mediaList)
						})
					else:
						logging.debug("{}: Bad Signature".function(cmd))
						response = self._getBadSigResponse(cmd)
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
				##
				# getMediaTypes
				##
				elif cmd == "getMediaTypes":
					logging.debug("Got POST command 'getMediaTypes'")
					sigCheck = utils.hmacSHA256(utils.hmacSHA256("getMediaTypes", psk), utils.hmacSHA256(xDate, username))
					if sigCheck == xSignature:
						logging.debug("getMediaTypes: Good Signature")
						mediaTypeList = db.lsMediaTypes()
						response = dumps({
							"status": "OK",
							"command": "getMediaTypes",
							"mediaTypeList": mediaTypeList,
							"mediaTypeListCount": len(mediaTypeList)
						})
					else:
						logging.debug("getMediaTypes: Bad Signature")
						response = dumps({
							"status": "ERR",
							"command": "getMediaTypes",
							"code": 401,
							"message": "Bad signature"
						})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}

				elif cmd == "getFileTypes":
					logging.debug("Got POST command 'getFileTypes'")
					fileTypeList = db.lsFileTypes()
					response = dumps({
						"status": "OK",
						"command": "getFileTypes",
						"fileTypeList": fileTypeList,
						"fileTypeListCount": len(fileTypeList)
					})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
				elif cmd == "getTags":
					logging.debug("Got POST command 'getTags'")
					tags = db.getTagAndCountList()
					ts = int(time())
					response = dumps({
						"status": "OK",
						"command": "getTags",
						"tagList": tags,
						"tagListCount": len(tags),
						"time": ts
					})
					responseStruct = {"status": status, "timestamp": ts, "response": response}

				elif cmd == "test":
					response = dumps({
						"status": "OK",
						"command": "test",
						"return": "Hello, from test",
					})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
				elif cmd == "showImage":
					logging.debug("Got POST command '{}'".format(cmd))
					if self._checkSignature(xSignature, cmd, psk, xDate, username) is True:
						sha256Hash = requestBody.get("sha256Hash", [None])[0]
						username = requestBody.get("username", [None])[0]
						thumbSize = requestBody.get("thumbSize", [None])[0]
						psk = db.fetchPSK(username)["secret"]
						logging.debug("Fetching tumbSize ({})".format(thumbSize))
						sigCheck = utils.hmacSHA256(utils.hmacSHA256("showImage", psk), utils.hmacSHA256(xDate, username))
						url = "/f/" + utils.getSecureDownloadKey(utils.getRelativeMediaFile(db.getThumbnail(sha256Hash, thumbSize)), self._secureDownloadKey) 
						response = dumps({
							"status": "OK",
							"command": cmd,
							"imageURL": url
						})
					else:
						logging.debug("showImage: Bad Signature")
						response = dumps({
							"status": "ERR",
							"command": cmd,
							"code": 401,
							"message": "Bad signature"
						})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
				elif cmd == "auth":
					logging.debug("Got POST command 'auth'")
					username = requestBody.get("username", [None])[0]
					auth = requestBody.get("auth", [None])[0]
					logging.debug("AUTH: got username ({}) and auth ({})".format(username, auth))
					TOTPAuth = TOTP()
					tokPass = "false"
					if TOTPAuth.auth(db.fetchUserSecret(username), auth) is True:
						tokPass = "true"
						logging.debug("AUTH: Token OK")
					else:
						logging.debug("AUTH: Token BAD")
					
					psk = utils.hmacSHA256(auth, username)
					db.updatePSK(username, psk)
					logging.debug("AUTH: Updated PSK")
					response = dumps({
						"status": "OK",
						"psk": psk
					})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
				else:
					response = dumps({
						"status": "ERR",
						"message": "Bad or missing command"
					})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
					
						

			elif environ["REQUEST_METHOD"] == "GET":
				getString = parse_qs(environ["QUERY_STRING"])
				gets = {}
				for parts in getString:
					gets[parts] = getString[parts][0]
				logging.debug("Get String ({})".format(gets))
				if cmd == "image":
					sha256Hash = gets.get("sha256Hash", None)
					responseStruct = {
						"isData": True,
						"status": status, 
						"timestamp": int(time()), 
						"contentType": "image/png",
						"response": wp.convertImage(db, sha256Hash, self._pdriveFiles)
					}
				elif cmd == "ajax":
					if gets.get("func"):
						func = gets.get("func")
						if func == "showVideo":
							sha256Hash = gets.get("sha256Hash")
							response = dumps({"videoURL": wp.showVideo(db, sha256Hash)})
							responseStruct = {"status": status, "timestamp": int(time()), "response": response}
						elif func == "setMediaDescr":
							sha256Hash = gets.get("sha256Hash")
							descr = gets.get("descr")
							if descr is None:
								descr = ""
							response = dumps({"status": "OK", "descr": db.mediaDescr(sha256Hash, descr)})
							responseStruct = {"status": status, "timestamp": int(time()), "response": response}
						elif func == "mediaAddTag":
							sha256Hash = gets.get("sha256Hash")
							tag = gets.get("tag")
							try:
								db.mediaAddTag(sha256Hash, tag)
								response = dumps({"status": "OK"})
								responseStruct = {"status": status, "timestamp": int(time()), "response": response}
							except:
								response = dumps({"status": "ERR"})
								responseStruct = {"status": status, "timestamp": int(time()), "response": response}
								raise
						elif func == "mediaRmTag":
							sha256Hash = gets.get("sha256Hash")
							tag = gets.get("tag")
							try:
								db.mediaRMTag(sha256Hash, tag)
								response = dumps({"status": "OK"})
								responseStruct = {"status": status, "timestamp": int(time()), "response": response}
							except:
								response = dumps({"status": "ERR"})
								responseStruct = {"status": status, "timestamp": int(time()), "response": response}
								raise
						elif func == "tagAdd":
							tag = gets.get("tag")
							try:
								db.addTag(tag)
								response = dumps({"status": "OK"})
								responseStruct = {"status": status, "timestamp": int(time()), "response": response}
							except:
								response = dumps({"status": "ERR"})
								responseStruct = {"status": status, "timestamp": int(time()), "response": response}
							
						else:
							response = dumps({
								"status": "ERR",
								"message": "Bad or missing command"
							})
							responseStruct = {"status": status, "timestamp": int(time()), "response": response}
					else:
						response = dumps({
							"status": "ERR",
							"message": "Bad or missing command"
						})
						responseStruct = {"status": status, "timestamp": int(time()), "response": response}
				else:
					response = dumps({
						"status": "ERR",
						"message": "Bad or missing command"
					})
					responseStruct = {"status": status, "timestamp": int(time()), "response": response}
		except self.ExceptionCriticalError as e:
			responseStruct = {"status": e.status, "timestamp": e.ts, "response": e.errorMessage}
		
		psk = db.fetchPSK(username)
		try:
			timeDiff = int(time()) - int(psk["timestamp"])
			if timeDiff > 1800:
				logging.debug("PSK has expired")
				responseStruct["response"] = dumps({"status": "ERR", "code": "201", "message": "PSK has expired. User must re-auth"})
			logging.debug("Fetched PSK ({}) for user ({})".format(psk["secret"], username))
			signature = utils.hmacSHA256(str(responseStruct["timestamp"]) + "::" + psk["secret"] + "::" + username, responseStruct["response"])
		except TypeError:
			logging.debug("None type turned. Barfing and scrubbbing");
			responseStruct["response"] = ""
			signature = ""
		except:
			raise

		try:
			if responseStruct["isData"] is True:
				responseHeaders = [
					('x-signature', signature), 
					('Date', format_date_time(responseStruct["timestamp"])), 
					('Content-Type', responseStruct["contentType"]), 
					('Content-Length', str(len(responseStruct["response"])))
				]
		except:
			responseHeaders = [
				('x-signature', signature), 
				('Date', format_date_time(responseStruct["timestamp"])),
				('Content-Type', contentType), 
				('Content-Length', str(len(responseStruct["response"])))
			]

		start_response(responseStruct["status"], responseHeaders)
		return [responseStruct["response"]]

